
/***************************************************************************
                          octopus.c  -  description
                             -------------------
    begin                : Aug  2007
    copyright            : (C) 2007 by empronix
    autho                : Benedikt Sauter, sauter@empronix.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software, you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License           *
 *   version 2.1 as published by the Free Software Foundation.             *
 *                                                                         *
 ***************************************************************************/

/**
    \mainpage liboctopus API documentation

    Library to talk to octopus devices. You find the latest versions of liboctopus at
    http://www.empronix.org/

    The library is easy to use. Have a look at this short example:
    \include simple.c

    More examples can be found in the "examples" directory.
*/

/** \addtogroup liboctopus */

/* @{ */

#include <string.h>
#include <errno.h>
#include <usb.h>

#include "octopus.h"
#include "../../firmware/protocol.h"

#define octopus_error_return(code, str) do {  \
        octopus->error_str = str;             \
        return code;                       \
        } while(0);

//////////////////////////////////////////////////////////////////////////////

static int octopus_open_dev (struct octopus_context *octopus, struct usb_device *dev)
{
	if (octopus == NULL)
		octopus_error_return (-1, "octopus handle is wrong or missing");

	if (dev == NULL)
		octopus_error_return (-2, "device handle is wrong or missing");

	if (!(octopus->usb_handle = usb_open (dev)))
		octopus_error_return (-3, "can't open usb device");

	if (!(usb_set_configuration (octopus->usb_handle, dev->config[0].bConfigurationValue) == 0))
		octopus_error_return (-4, "can't set configuration for given usb device");

	if (!(usb_claim_interface (octopus->usb_handle, 0) == 0))
		octopus_error_return (-5, "can't claim interface for given usb device");

	if (usb_set_altinterface (octopus->usb_handle, 0))
		octopus_error_return (-6, "can't set altinterface for given usb device");

//	if (usb_resetep (octopus->usb_handle, 1))
//		octopus_error_return (-7, "can't reset endpoint 0x01");

	// we need this, to reset the data0/data1 toggle in the device
	if (usb_resetep (octopus->usb_handle, 0x81))
		octopus_error_return (-7, "can't reset endpoint 0x81");

	return 1;
}

int octopus_init (struct octopus_context *octopus)
{
	if (octopus == NULL)
	{
		// octopus_error_return (-1, "octopus not valid"); BUG
		return -1;
	}
	octopus->error_str = "no error";
	octopus->usb_handle = NULL;
	return 1;
}

int octopus_open (struct octopus_context *octopus)
{
	if (octopus_open_id (octopus, VID, PID) < 0)
		return -1;
	return 1;
}

int octopus_open_id (struct octopus_context *octopus, int vendor, int product)
{
	struct usb_bus *busses;
	struct usb_bus *bus;
	struct usb_device *dev;

	usb_init ();

	if (usb_find_busses () < 0)
		octopus_error_return (-1, "usb_find_busses() failed");
	if (usb_find_devices () < 0)
		octopus_error_return (-2, "usb_find_devices() failed");

	busses = usb_get_busses ();
	if (busses == NULL)
		octopus_error_return (-3, "usb_get_busses() failed");

	for (bus = busses; bus; bus = bus->next)
		for (dev = bus->devices; dev; dev = dev->next)
			if (dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product)
				return octopus_open_dev (octopus, dev);

	octopus_error_return (-4, "could not find octopus device with pid and vid");
	return -1;
}

int octopus_open_serial (struct octopus_context *octopus, char *serial)
{
	struct usb_bus *busses;
	struct usb_dev_handle *usb_handle;
	struct usb_bus *bus;
	struct usb_device *dev;
	char    usbserial[64];
	int     length = 0;

	usb_init ();

	if (usb_find_busses () < 0)
		octopus_error_return (-1, "usb_find_busses() failed");
	if (usb_find_devices () < 0)
		octopus_error_return (-2, "usb_find_devices() failed");

	busses = usb_get_busses ();
	if (busses == NULL)
		octopus_error_return (-3, "usb_get_busses() failed");

	for (bus = busses; bus; bus = bus->next)
	{
		for (dev = bus->devices; dev; dev = dev->next)
		{
			if (dev->descriptor.idVendor == VID && dev->descriptor.idProduct == PID)
			{
				usb_handle = usb_open (dev);
				usb_set_configuration (usb_handle, 0);
				length = usb_get_string_simple (usb_handle, 3, usbserial, 64);
				usb_close (usb_handle);

				if (strncmp (serial, usbserial, length) == 0)
				{
					octopus_open_dev (octopus, dev);
					return 1;
				}
			}
		}
	}
	octopus_error_return (-4, "could not find octopus device with serial number");
	return -1;
}

int octopus_close (struct octopus_context *octopus)
{
	if (octopus == NULL)
		octopus_error_return (-1, "octopus is not a valid handle");

	if (usb_release_interface (octopus->usb_handle, 0) < 0)
		octopus_error_return (-3, usb_strerror ());

	// brutal un-configure the device. Only this garantees, that set_config is not skipped in open_dev
//	if (!(usb_set_configuration (octopus->usb_handle, 0) == 0))
//		octopus_error_return (-4, "can't reset configuration for given usb device");

	if (usb_close (octopus->usb_handle) < 0)
		octopus_error_return (-2, usb_strerror ());

	return 1;
}

static int octopus_message (struct octopus_context *octopus, char *msg, int msglen, char *answer, int answerlen)
{
	if (usb_bulk_write (octopus->usb_handle, 1, msg, msglen, 500) < msglen)
		octopus_error_return (-1, "transfer error occurred");
	
	if (answerlen > 0)
	{
		int rc;
		rc = usb_bulk_read (octopus->usb_handle, 0x81, answer, answerlen, 1000);
		if (rc < 0)
		{
			char *s = usb_strerror ();
			octopus_error_return (-2, s);
		}

		if (answer[1] == RSP_OK)
		{
			return rc;	// return response length
		}
		else if (answer[1] == RSP_UNKOWN_CMD)
		{
			octopus_error_return (-1, "unkown command");
		}
		else if (answer[1] == RSP_UNKOWN_PIN)
		{
			octopus_error_return (-2, "unkown pin");
		}
		else if (answer[1] == RSP_WRONG_PIN_CONFIG)
		{
			octopus_error_return (-3, "pin is in wrong configuration");
		}
		else if (answer[1] == RSP_ERROR)
		{
			octopus_error_return (-4, "error in usb device");
		}
		else if (answer[1] == RSP_TIMEOUT)
		{
			octopus_error_return (-4, "timeout error in usb device"); 
		}
		else
		{
			octopus_error_return (-5, "unknown error");
		}
	}
	return 1;
}

int octopus_reset (struct octopus_context *octopus)
{
	return -1;
}
int octopus_status (struct octopus_context *octopus)
{
	return -1;
}
int octopus_logging (struct octopus_context *octopus)
{
	return -1;
}

int octopus_get_hwdesc (struct octopus_context *octopus, char *desc)
{
	char    msg[] = { CMD_GET_HW_ID, 0x00, 0x00 };
	char    answer[64] = { 0 };

	if (octopus_message (octopus, msg, sizeof (msg), answer, sizeof (answer)) < 0)
		return -1;

	if (answer[2] > 0 && answer[2] < 12)
		memcpy (desc, answer + 3, answer[2]);
	return 1;
}

int octopus_io_init (struct octopus_context *octopus, int pin)
{
	char    answer[64];
	char    msg[] = { CMD_IO_INIT_PIN, 0x01, 0x00 };
	msg[2] = (char) pin;
	if (octopus_message (octopus, msg, 3, answer, sizeof (answer)) > 0)
		return 1;
	else
		return -1;
}

int octopus_io_init_port (struct octopus_context *octopus, int port)
{
	char    msg[] = { CMD_IO_INIT_PORT, 0x01, 0x00 };
	char    answer[2];

	msg[2] = (char) port;
	if (octopus_message (octopus, msg, 3, answer, sizeof (answer)) > 0)
		return 1;
	else
		return -1;
}

int octopus_io_set_port_direction_out (struct octopus_context *octopus, int port, unsigned char mask)
{
	char    answer[2];
	char    msg[] = { CMD_IO_PORT_DIRECTION_OUT, 0x02, 0x00, 0x00 };
	msg[2] = (char) port;
	msg[3] = mask;
	if (octopus_message (octopus, msg, 4, answer, sizeof (answer)) > 0)
		return 1;
	else
		return -1;
}

int octopus_io_set_port_direction_in (struct octopus_context *octopus, int port, unsigned char mask)
{
	char    answer[2];
	char    msg[] = { CMD_IO_PORT_DIRECTION_IN, 0x02, 0x00, 0x00 };
	msg[2] = (char) port;
	msg[3] = mask;
	if (octopus_message (octopus, msg, 4, answer, sizeof (answer)) > 0)
		return 1;
	else
		return -1;
}

int octopus_io_set_port_direction_tri (struct octopus_context *octopus, int port, unsigned char mask)
{
	char    answer[2];
	char    msg[] = { CMD_IO_PORT_DIRECTION_TRI, 0x02, 0x00, 0x00 };
	msg[2] = (char) port;
	msg[3] = mask;
	if (octopus_message (octopus, msg, 4, answer, sizeof (answer)) > 0)
		return 1;
	else
		return -1;
}

int octopus_io_set_pin_direction_out (struct octopus_context *octopus, int pin)
{
	char    answer[3];
	char    msg[] = { CMD_IO_PIN_DIRECTION_OUT, 0x01, 0x00 };
	msg[2] = (char) pin;
	if (octopus_message (octopus, msg, 3, answer, sizeof (answer)) > 0)
		return 1;
	else
		return -1;
}

int octopus_io_set_pin_direction_in (struct octopus_context *octopus, int pin)
{
	char    answer[2];
	char    msg[] = { CMD_IO_PIN_DIRECTION_IN, 0x01, 0x00 };
	msg[2] = (char) pin;
	if (octopus_message (octopus, msg, 3, answer, sizeof (answer)) > 0)
		return 1;
	else
		return -1;
}

int octopus_io_set_pin_direction_tri (struct octopus_context *octopus, int pin)
{
	char    answer[2];
	char    msg[] = { CMD_IO_PIN_DIRECTION_TRI, 0x01, 0x00 };
	msg[2] = (char) pin;
	if (octopus_message (octopus, msg, 3, answer, sizeof (answer)) > 0)
		return 1;
	else
		return -1;
}

unsigned char octopus_io_get_port (struct octopus_context *octopus, int port)
{
	char    answer[3];
	char    msg[] = { CMD_IO_PORT_GET, 0x01, 0x00 };
	msg[2] = (char) port;
	if (octopus_message (octopus, msg, 3, answer, sizeof (answer)) > 0)
		return (unsigned char) answer[2];
	else
		return 0;				// no real error return !
}

typedef unsigned char uint8_t;

int octopus_io_rc_get_page (struct octopus_context *octopus, uint8_t page, unsigned char *buf)
{
	char    answer[2+256];
	char    msg[] = { (char) CMD_RC_PAGE_GET, 0x01, 0x00 };
	int     i, rc;

	msg[2] = (char) page;
	rc = octopus_message (octopus, msg, 3, answer, sizeof (answer));

	for (i = 0; i < sizeof (answer) - 2; i++)
		buf[i] = (unsigned char) answer[2 + i];
	
	if (rc < 0)
		return -1;
	return 1;
}

int octopus_io_get_port_bulk (struct octopus_context *octopus, int port, int number, unsigned char *buf)
{
	return -1;
}

int octopus_io_set_port (struct octopus_context *octopus, int port, unsigned char value)
{
	char    answer[2];
	char    msg[] = { CMD_IO_PORT_SET, 0x01, 0x00, 0x00 };
	msg[2] = (char) port;
	msg[3] = (char) value;
	if (octopus_message (octopus, msg, 4, answer, sizeof (answer)) > 0)
		return 1;
	else
		return -1;
}

int octopus_io_set_port_bulk (struct octopus_context *octopus, int port, int number, unsigned char *buf)
{
	return -1;
}

int octopus_io_set_pin (struct octopus_context *octopus, int pin, int value)
{
	char    answer[3];
	char    msg[] = { CMD_IO_PIN_SET, 0x01, 0x00, 0x00 };
	msg[2] = (char) pin;
	msg[3] = (char) value;
	if (octopus_message (octopus, msg, 4, answer, sizeof (answer)) > 0)
		return 1;
	else
		return -1;
}

int octopus_io_set_pin_bulk (struct octopus_context *octopus, int pin, int number, int *buf)
{
	return -1;
}

int octopus_io_get_pin (struct octopus_context *octopus, int pin)
{
	char    answer[2];
	char    msg[] = { CMD_IO_PIN_GET, 0x01, 0x00 };
	msg[2] = (char) pin;
	msg[3] = 0x00;
	if (octopus_message (octopus, msg, 3, answer, sizeof (answer)) > 0)
		return (int) answer[2];
	else
		return -1;
}

int octopus_io_get_pin_bulk (struct octopus_context *octopus, int pin, int number, int *buf)
{
	return -1;
}

/// part: adc
int octopus_adc_init (struct octopus_context *octopus, int pin)
{
	char    answer[2];
	char    msg[] = { CMD_ADC_INIT_PIN, 0x01, 0x00 };
	msg[2] = (char) pin;
	if (octopus_message (octopus, msg, 3, answer, sizeof (answer)) > 0)
		return 1;
	else
		return -1;
}

int octopus_adc_get (struct octopus_context *octopus, int pin)
{
	char    answer[5];
	char    msg[] = { CMD_ADC_GET, 0x01, 0x00 };
	msg[2] = (char) pin;
	if (octopus_message (octopus, msg, 3, answer, sizeof (answer)) > 0)
	{
		//unsigned char low = answer[3]; 
		//unsigned char high = answer[2]; 
		//return ((int)low)+(((int)answer[2])*256);

		int     low = answer[3];

		return low;
		//int j=(int)answer[2];
		//return j;

		if (answer[2] == 0x00)
			return low;
		else if (answer[2] == 0x01)
			return low + 256;
		else if (answer[2] == 0x02)
			return low + 512;
		else if (answer[2] == 0x03)
			return low + 768;
		else
			return 0;

		//return i+(((int)answer[2])*256);
		//return j;
		//+(((int)answer[2])*256);
		//return ((int)answer[2])*256;
		//return 1023;
	}
	else
		return -1;
}

int octopus_adc_ref (struct octopus_context *octopus, int ref)
{
	char    answer[3];
	char    msg[] = { CMD_ADC_REF, 0x01, 0x00 };
	msg[2] = (char) ref;
	if (octopus_message (octopus, msg, 3, answer, sizeof (answer)) > 0)
		return (int) answer[2];
	else
		return -1;
}

int octopus_adc_get_bulk (struct octopus_context *octopus, int pin, int number, int *buf)
{
	return 1;
}

int octopus_i2c_init (struct octopus_context *octopus)
{
	char    answer[2];
	char    msg[] = { CMD_I2C_INIT, 0x00, 0x00 };
	if (octopus_message (octopus, msg, 3, answer, sizeof (answer)) > 0)
		return 1;
	else
		return -1;
}

int octopus_i2c_deinit (struct octopus_context *octopus)
{
	char    answer[2];
	char    msg[] = { CMD_I2C_DEINIT, 0x00, 0x00 };
	if (octopus_message (octopus, msg, 3, answer, sizeof (answer)) > 0)
		return 1;
	else
		return -1;
}

int octopus_i2c_set_bitrate (struct octopus_context *octopus, int speed)
{
	char    answer[2];
	char    msg[] = { CMD_I2C_BITRATE, 0x01, 0x00 };
	msg[2] = (char) speed;
	if (octopus_message (octopus, msg, 3, answer, sizeof (answer)) > 0)
		return (int) answer[2];
	else
		return -1;
}

int octopus_i2c_send_bytes (struct octopus_context *octopus, unsigned char *buf, int len, int timeout)
{
	char    answer[2];

	// cmd = cmd,len,timeout,address,data1,data2,..)
	char    msg[64] = { CMD_I2C_SEND_BYTES, 0x01, 0x00 };
	int     i;

	if (len > 60)
	{
		return -1;				// max 60 bytes
	}

	msg[1] = (char) (len + 4);
	msg[2] = (char) timeout;
	//msg[3] = (char)address;

	// build send array

	for (i = 0; i < len; i++)
		msg[i + 4] = (char) buf[i];

	if (octopus_message (octopus, msg, len + 4, answer, sizeof (answer)) > 0)
		return (int) answer[2];
	else
		return -1;
}

int octopus_i2c_send_byte (struct octopus_context *octopus, unsigned char buf, int timeout)
{
	return octopus_i2c_send_bytes (octopus, &buf, 1, 1);	// 1ms
}

unsigned char octopus_i2c_receive_byte (struct octopus_context *octopus, int address, int timeout)
{
	unsigned char tmp[1];

	if (octopus_i2c_receive_bytes (octopus, address, tmp, 1, 1) > 0)
		return tmp[1];
	else
		return -1;
}

int octopus_i2c_receive_bytes (struct octopus_context *octopus, int address, unsigned char *buf, int len, int timeout)
{
	char    msg[64] = { CMD_I2C_RECV_BYTES, 0x01, 0x00 };
	char    answer[64];
	int     i;

	if (len > 60)
	{
		return -1;				// max 60 bytes
	}

	msg[1] = (char) (len + 4);
	msg[2] = (char) timeout;
	msg[3] = (char) address;

	if (octopus_message (octopus, msg, 4, answer, len + 3) > 0)
	{
		for (i = 0; i < len; i++)
			buf[i] = (unsigned char) answer[i + 3];
		return (int) answer[2];
	}
	else
		return -1;
}

int octopus_i2c_send_start (struct octopus_context *octopus)
{
	char    answer[2];
	char    msg[] = { CMD_I2C_SEND_START, 0x00, 0x00 };
	if (octopus_message (octopus, msg, 3, answer, sizeof (answer)) > 0)
		return 1;
	else
		return -1;

	return 1;
}

int octopus_i2c_send_stop (struct octopus_context *octopus)
{
	char    answer[2];
	char    msg[] = { CMD_I2C_SEND_STOP, 0x00, 0x00 };
	if (octopus_message (octopus, msg, 3, answer, sizeof (answer)) > 0)
		return 1;
	else
		return -1;
}

/// part: SPI
int octopus_spi_init (struct octopus_context *octopus)
{
	char    answer[2];
	char    msg[] = { CMD_SPI_INIT, 0x00, 0x00 };
	if (octopus_message (octopus, msg, 3, answer, 2) > 0)
		return 1;
	else
		return -1;
}

int octopus_spi_deinit (struct octopus_context *octopus)
{
	char    answer[2];
	char    msg[] = { CMD_SPI_DEINIT, 0x00, 0x00 };
	if (octopus_message (octopus, msg, 3, answer, 2) > 0)
		return 1;
	else
		return -1;
}

int octopus_spi_speed (struct octopus_context *octopus, int speed)
{
	return -1;
}

int octopus_spi_send (struct octopus_context *octopus, unsigned char *buf, int length)
{
	int     i;
	char    answer[2];

	// cmd = cmd,len,timeout,address,data1,data2,..)
	char    msg[64] = { CMD_SPI_SEND, 0x01, 0x00 };
	msg[1] = (char) (length);

	if (length > 60)
	{
		return -1;				// max 60 bytes
	}

	// build send array

	for (i = 0; i < length; i++)
		msg[i + 2] = (char) buf[i];

	if (octopus_message (octopus, msg, 2 + length, answer, 2) > 0)
		return 1;
	else
		octopus_error_return (-1, octopus->error_str);

	return 1;
}

int octopus_spi_recv (struct octopus_context *octopus, unsigned char *buf, int length)
{
	char    answer[64];

	char    msg[64] = { CMD_SPI_RECV, 0x01, 0x00 };
	msg[1] = (char) (length);

	if (length > 60)
	{
		return -1;				// max 60 bytes
	}

	if (octopus_message (octopus, msg, 2, answer, 2 + length) > 0)
	{
		int     i;
		for (i = 0; i < length; i++)
			buf[i] = answer[i + 2];

		return 1;
	}
	else
		octopus_error_return (-1, octopus->error_str);
}

int octopus_spi_send_and_recv (struct octopus_context *octopus, unsigned char *buf, int length)
{
	char    answer[2];
	int     i;

	// cmd = cmd,len,timeout,address,data1,data2,..)
	char    msg[64] = { CMD_SPI_SEND_AND_RECV, 0x01, 0x00 };
	msg[1] = (char) (length);

	if (length > 60)
	{
		return -1;				// max 60 bytes
	}

	// build send array
	for (i = 0; i < length; i++)
		msg[i + 2] = (char) buf[i];

	if (octopus_message (octopus, msg, 2 + length, answer, 2 + length) > 0)
	{

		for (i = 0; i < length; i++)
			buf[i] = answer[i + 2];

		return 1;
	}
	else
		octopus_error_return (-1, octopus->error_str);

	return 1;
}


/// part: UART
int octopus_uart_init (struct octopus_context *octopus, int uartport)
{
	return -1;
}
int octopus_uart_deinit (struct octopus_context *octopus, int uartport)
{
	return -1;
}

int octopus_uart_default (struct octopus_context *octopus, int uartport)
{
	return -1;
}
int octopus_uart_speed (struct octopus_context *octopus, int uartport, int speed)
{
	return -1;
}
int octopus_uart_stopbits (struct octopus_context *octopus, int uartport, int stopbits)
{
	return -1;
}
int octopus_uart_databits (struct octopus_context *octopus, int uartport, int databits)
{
	return -1;
}
int octopus_uart_parity (struct octopus_context *octopus, int uartport, int parity)
{
	return -1;
}

int octopus_uart_send (struct octopus_context *octopus, int uartport, unsigned char *buf, int length)
{
	return -1;
}
int octopus_uart_recv (struct octopus_context *octopus, int uartport, unsigned char *buf, int length)
{
	return -1;
}
